
import aei.AeRegistry
import aei.AeiService
import aei.StudySearchSchedule
import aei.StudySearchResult
import aei.SeriesSearchResult
import hce.HceService
import hce.core.composition.* // Composition y EventContext

class RunScheduledStudySearchJob {
    
    def aeiService
    def hceService
    
    def timeout = 90000l // execute job once in 10 seconds
    def startDelay = 60000l // empieza al minuto
    /*
    static triggers = {
        simple name:'simpleTrigger', startDelay:10000, repeatInterval: 30000, repeatCount: 10
        cron name:'cronTrigger', startDelay:10000, cronExpression: '0/6 * 15 * * ?'
        custom name:'customTrigger', triggerClass:MyTriggerClass, myParam:myValue, myAnotherParam:myAnotherValue
    }
    */
    
    def execute()
    {
        //println "... .......................... ..."
        //println "... RunScheduledStudySearchJob ..."
        //println "... .......................... ..."
        
        def pacss = AeRegistry.list() // pacs configurados
        def schedules = StudySearchSchedule.list() // busquedas programadas
        
        def pacsErrors = [:] // Se usa al final para saber si no se pudo conectar al pacs
        // Si actualizo el pacs adentro de esta iteracion por pacs
        // me dice que fue actualizado por otra transaccion.
        // Asi que uso el pacsErrors para actualizar el pacs al finalizar la iteracion.

        pacss.each { pacs ->
        
            if (pacs.active)
            {
                schedules.each { schedule ->
                
                    if (schedule.episodioId)
                    {
                        def name = schedule.subject.identities.find{ it.purpose == 'PersonName'}
                    
                        // TODO: retries si hay un problema, cant configurable
                        
                        // Un intento con cada id, si encuentro con uno, no ejecuto los otros.
                        def iter = schedule.subject.ids.iterator()
                        while (iter.hasNext())
                        {
                            def pid = iter.next()
                            try {
                                /* busca a nivel de paciente. es para ver si el paciente esta en el pacs, no para obtener el estudio...
                                def results = aeiService.sendDICOMQuery1( pacs,
                                              ((name?.primerNombre) ? name?.primerNombre : null), // nombre 1
                                              ((name?.primerApellido) ? name?.primerApellido : null), // apellido 1
                                              pid.extension,
                                              null, //params.studyDateFrom_year  as Integer,
                                              null, //params.studyDateFrom_month as Integer,
                                              null, //params.studyDateFrom_day   as Integer,
                                              null, //params.studyDateTo_year    as Integer,
                                              null, //params.studyDateTo_month   as Integer,
                                              null //params.studyDateTo_day     as Integer
                                          )
                                */
                                
                                // busca a nivel de estudio
                                def results = aeiService.sendDICOMQuery2(
                                    pacs,
                                    ((name?.primerNombre) ? name?.primerNombre : null), // nombre 1
                                    ((name?.primerApellido) ? name?.primerApellido : null), // apellido 1
                                    pid.extension,
                                    null, //String studyID,
                                    null, //params.studyDateFrom_year  as Integer,
                                    null, //params.studyDateFrom_month as Integer,
                                    null, //params.studyDateFrom_day   as Integer,
                                    null, //params.studyDateTo_year    as Integer,
                                    null, //params.studyDateTo_month   as Integer,
                                    null //params.studyDateTo_day     as Integer
                                )
                                
                                // Marco como que no hubo error para este pacs
                                pacsErrors[pacs] = false
                                
                                if (results.size() > 0)
                                {
                                    results.each { result ->
                                    
                                        println "   resutl: " + result
                                    
                                        
                                        // El id que se guarda en el result es el del partySelf del episodio, asi tengo 
                                        // vinculados los resultados al episodio y es mas facil mostrarlos para el episodio actual.
                                        def compo = Composition.get( schedule.episodioId )
                                        def pself = hceService.getPartySelfFromComposition( compo ) // Puede ser null si el episodio no tiene paciente, pero no estaria aca si no se hubiera identificado un paciente antes y si la busqueda programada no tuviera el episodioId seteado.
                                        
                                        
                                        // No guardar un resultado que ya se obtuvo 
                                        def resExists = StudySearchResult.withCriteria {
                                           /* Con esta condicion si la persona tenia dos estudios, solo encontraba uno...
                                              eq('patientId', pself.externalRef.objectId.value)
                                              eq('studyDate', result['80020'])
                                            * 
                                            */
                                            eq('patientId', pself.externalRef.objectId.value) // creo que solo con el studyId alcanza
                                            eq('studyId', result['200010'])
                                        }
                                    
                                        if (resExists.size() == 0 && pself)
                                        {
                                            /*
                                             * 80005:ISO_IR 100, 
                                             * 80020:20010105, 
                                             * 80030:083501, 
                                             * 80050:0000000001, 
                                             * 80052:STUDY, 
                                             * 80054:SERVERAE, 
                                             * 80056:ONLINE, 
                                             * 80061:CT, 
                                             * 100010:MISTER^CT, 
                                             * 100020:2178309, 
                                             * 100032:, 
                                             * 100040:, 
                                             * 20000d:1.2.840.113619.2.30.1.1762295590.1623.978668949.886, StudyInstanceUID
                                             * 200010:40933, 
                                             * 201200:, 
                                             * 201202:, 
                                             * 201204:
                                             * ]
                                             */
                                            def ssres = new StudySearchResult
                                            (
                                                patientName:  result['100010'], // 100010 => MISTER^CT
                                                patientId:    pself.externalRef.objectId.value, //result['100020'], // FIXME: hacer que el id que se guarda sea el del partySelf del episodio, asi tengo vinculados los resutlados al episodio.
                                                birthDate:    result['100030'],
                                                sex:          result['100040'],
                                                studyDate:    result['80020'],
                                                studyId:      result['200010'],
                                                seriesNumber: result['201206'],
                                                imagesNumber: result['201208'],
                                                studyDescription: result['81030'],
                                                modalitiesInStudy: result['80061'],
                                                source:       pacs // Desde que PACS se obtuvo el resultado
                                            )

                                            
                                            // 17/04/2010
                                            // =======================================================
                                            // ==== NUEVO: busca tambien series por pedido del tutor
                                            // ==== de mostrar todas las series al seleccionar una.
                                            // =======================================================
                                            
                                            // TODO: si hay un error en la comunicacion aca, el estudio
                                            //       se queda sin series cuando en realidad tiene.
                                            
                                            println "--------------------------------------------------"
                                            println "--------------------------------------------------"
                                            println "---- BUSCANDO series para studyID: " + ssres.studyId
                                            
                                            def resultsSeries = aeiService.sendDICOMQuery3(
                                                    pacs, // source
                                                    ssres.studyId, // studyId
                                                    null, // studyUID
                                                    null ) // studyDate

                                            resultsSeries.each { resultSerie ->
                                                
                                                def seriessr = new SeriesSearchResult
                                                (
                                                    modality:    resultSerie['80060'],
                                                    description: resultSerie['8103e'],
                                                    number:      resultSerie['200011'],
                                                    elements:    resultSerie['201209'],
													// Si no viene en resultSerie (pasa en COMEPA) lo saco de resultado de estudio que si deberia venir.
                                                    studyUID:    ((resultSerie['20000d']) ? resultSerie['20000d'] : result['20000d']),
                                                    serieUID:    resultSerie['20000e']
                                                )
                                                
                                                ssres.addToSeries( seriessr )
                                                
                                                // TODO: salvar seriessr???
                                                //if (!seriessr.save() ....
                                            }
                                            
                                            println "---- FIN buscar series para studyID: " + ssres.studyId
                                            println "--------------------------------------------------"
                                            println "--------------------------------------------------"
                                            
                                            // =======================================================
                                            // =======================================================
                                            
                                            if (!ssres.save())
                                            {
                                                println "   RunScheduledStuydSearch: error al salvar result " + ssres.errors + "---------"
												println "   errores en series: "
												ssres.series.each{ unaserie -> 
												   println "    - " + unaserie.errors
                                                }
												println "   RunScheduledStuydSearch ---------------------------------------"
                                            }
                                        }
                                        else
                                        {
                                            println "   RunScheduledStuydSearch: estudio ya encontrado"
                                            
                                            /*
                                            // En este caso resExists tiene algun elemento.
                                            // resExists deberia tener exactamente 1 elemento
                                            // porque se busca por studyId.
                                            if ( resExists[0] && resExists[0].series.size()==0 )
                                            {
                                                // 17/04/2010
                                                // =======================================================
                                                // ==== NUEVO: busca tambien series por pedido del tutor
                                                // ==== de mostrar todas las series al seleccionar una.
                                                // =======================================================
                                                
                                                // TODO: si hay un error en la comunicacion aca, el estudio
                                                //       se queda sin series cuando en realidad tiene.
                                                
                                                def resultsSeries = aeiService.sendDICOMQuery3(
                                                        pacs, // source
                                                        resExists[0].studyId, // studyId
                                                        null, // studyUID
                                                        null ) // studyDate
                                                
                                                resultsSeries.each { resultSerie ->
                                                    
                                                    def seriessr = new SeriesSearchResult(
                                                    modality:    resultSerie['80060'],
                                                    description: resultSerie['8103e'],
                                                    number:      resultSerie['200011'],
                                                    elements:    resultSerie['201209'],
                                                    serieUID:    resultSerie['20000e']
                                                    )
                                                    
                                                    resExists[0].addToSeries( seriessr )
                                                    
                                                    // TODO: salvar seriessr???
                                                    //if (!seriessr.save() ....
                                                }
                                                // =======================================================
                                                // =======================================================
                                                
                                                if (!resExists[0].save())
                                                {
                                                    println "   RunScheduledStuydSearch: error al salvar result 2 (agrego series a resultado existente) " + resExists[0].errors
                                                }
                                            }
                                            */
                                        }
                                    }
                                    break // no busca por otros ids
                                }
                            }
                            catch (Exception e)
                            {
                                println "   RunScheduledStuydSearch: Exception: " + e.getMessage()
                                
                                // FIXME: para ver si anda mal el QR deberia tirar la except aca
                                //        pero la catchea adentro...
                                
                                // Hubo error para este pacs
                                pacsErrors[pacs] = true
                            }
                        } // para cada id de la persona
                    } // si tengo el id del episodio en la busqueda programada
                    else
                    {
                        // busco a ver si hay un episodio para el paciente para obtener el episodioId
                        def compo = hceService.getActiveCompositionForPatient( schedule.subject )
                        
                        println "Composition encontrado para el paciente: " + compo
                        
                        // Pude identificar el episodio al que pertenece la persona
                        // configurada en la busqueda planificada (el episodio es necesario
                        // para que los resultados se asocien al id de la persona que esta
                        // guardado en el episodio (en el partySelf.externalRef.id).
                        // Ese id es el que voy a usar para saber si tengo que mostrar los resultados
                        // de la busqueda en el episodio que esta viendo el medico.
                        // Podria obtener directamente el id del partyRef del episodio pero tengo 
                        // que asegurarme que el episodio esta activo.
                        if (compo)
                        {
                            schedule.episodioId = compo.id
                        }
                    }
                } // para cada busqueda programada
            } // si el pacs esta activo
        } // para cada pacs configurado
        
        //println "PACS ERRORS: " + pacsErrors
        
        // Veo si ocurrieron
        pacsErrors.keySet().each { pacs ->
            if (pacsErrors[pacs])
            {
                // Si de un PACS no puede tener conexion luego de X intentos 
                // configurables, que no intente conectar a ese PACS nunca mas.
                // Volveria a intentar conectarse al PACS si desde la pantalla de
                // adminsitracion de AeRegistry lo habilito.
                def maxBadCC = 5 // TODO: sacarlo de config
                
                AeRegistry.withTransaction { status ->
                
                    pacs.badConnectionCount ++
                    if (pacs.badConnectionCount > maxBadCC)
                        pacs.active = false
                        
                    if (!pacs.save(flush:true))
                        println "   RunScheduledStudySearch: ERROR al actualizar aeRegistry: " + pacs.errors
                }
                
                // Creo que salva solito al cambiarle los valores de los campos...
                // Si le hago save me dice: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect
                //if (!pacs.save())
                //    println "RunScheduledStuydSearch: error al salvar PACS " + pacs.errors
            }
            else
            {
                // Si no hubo excepcion el servidor esta online, reseteo la cuenta
                // de conecciones para decir que esta conexion funciona.
                pacs.badConnectionCount = 0 // se guarda solo!
            }
        }
    }
}
